/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cadoc.cqe;

import java.util.Collection;
import java.util.LinkedList;

import org.cadoc.Main;
import org.cadoc.cqe.KnowledgeReceiver.Construct;
import org.cadoc.parser.LanguageToken;
import org.cadoc.parser.StatementHandler;
import org.cadoc.parser.statement.ClassDeclaration;
import org.cadoc.parser.statement.ImportStatement;
import org.cadoc.parser.statement.Modifier;
import org.cadoc.parser.statement.OperationCallStatement;
import org.cadoc.parser.statement.OperationDeclaration;
import org.cadoc.parser.statement.PackageDeclaration;
import org.cadoc.parser.statement.Statement;
import org.cadoc.parser.statement.VariableDeclaration;

public class StatementParserHandler implements StatementHandler {

	private KnowledgeReceiver codeKnowledge;
	
	// some context stack
	private String currentPackage;
	private String currentClass;
	private String currentOperation;
	
	private Collection<String> currentImports = new LinkedList<String>();
	
	
	
	public KnowledgeReceiver getCodeKnowledge() {
		return codeKnowledge;
	}

	public void setCodeKnowledge(KnowledgeReceiver codeKnowledge) {
		this.codeKnowledge = codeKnowledge;
	}

	
	public void endStatement(Statement statement) {
		// TODO Auto-generated method stub

	}

	public void startCompilationUnit(String fullName) {
		// TODO Auto-generated method stub

	}

	public void startStatement(Statement statement) {
		Main.stmtCount++;
		String fqn;
		
		if(statement instanceof PackageDeclaration){
			currentPackage = statement.getValue();
			currentImports.clear();
		}
		
		if(statement instanceof ImportStatement){
			currentImports.add(statement.getValue());
		}
		
		if(statement instanceof ClassDeclaration){
			ClassDeclaration cd = (ClassDeclaration)statement;
			fqn = currentPackage+"."+cd.getValue();
			currentClass = fqn;
			codeKnowledge.addConstruct(Construct.TYPE, fqn);
			for(String implInterface : cd.getImplementedInterfaces()){
				codeKnowledge.addImplements(fqn, implInterface);
			}
			codeKnowledge.addInherit(fqn, cd.getSuperClass());
		}
		
		if(statement instanceof VariableDeclaration){
			VariableDeclaration vd = (VariableDeclaration)statement;
			int facts = fetchFactsFromModifiers(vd.getModifiers());
			
			fqn = currentClass+"#"+vd.getValue();
			codeKnowledge.addConstruct(Construct.ELEMENT, fqn);
			codeKnowledge.addDeclaresField(currentClass, fqn);
			
			codeKnowledge.setType(Construct.ELEMENT, fqn, fetchFQType(vd.getType()));
			codeKnowledge.setFactVector(Construct.ELEMENT, fqn, facts);
		}
		
		if(statement instanceof OperationDeclaration){
			OperationDeclaration od = (OperationDeclaration)statement;
			// FIXME build proper fqn for operation (with params)
			fqn = currentClass+"."+od.getValue()+"(";
			boolean first=true;
			for(String[] param : od.getParameters()){
				if(!first) fqn+=", ";
				fqn+=fetchFQType(param[0]);
				first=false;
			}
			fqn+=")";
			currentOperation = fqn;

			codeKnowledge.setFactVector(Construct.OPERATION, fqn, fetchFactsFromModifiers(od.getModifiers()));
			codeKnowledge.setType(Construct.OPERATION, fqn, fetchFQType(od.getReturnType()));
			codeKnowledge.addDeclaresOperation(currentClass, fqn);
		}
		
		if(statement instanceof OperationCallStatement){
			OperationCallStatement ocs = (OperationCallStatement)statement;
			String callee = fetchFQType(ocs.getCallee())+"."+ocs.getValue()+"()";
			codeKnowledge.addCalls(currentOperation, callee);
		}

	}
	
	
	// private helper to determine the fully qualified type of a typed concepts.
	// usually look for imports or the java lang package or the set of primitive types
	private String fetchFQType(String type){

		String fqType = "";
		for(String primitive : LanguageToken.PRIMITIVE_TYPES){
			if(type.equals(primitive)){
				fqType = primitive;
			}
		}
		if(fqType.equals("")){
			for(String noImportClass : LanguageToken.CLASSES_WITH_NO_IMPORT){
				if(type.equals(noImportClass)){
					fqType = "java.lang."+noImportClass;
				}
				if(type.equals(noImportClass+"[]")){
					fqType = "java.lang."+noImportClass;
				}
			}
		}
		if(fqType.equals("")){
			fqType = currentPackage+"."+type;
		};
		
		return fqType;
	}
	
	private int fetchFactsFromModifiers(Iterable<Modifier> modifiers){
		int facts = 0;
		for(Modifier m : modifiers){
			switch(m){
			case ABSTRACT: 	facts |= FactVector.ABSTRACT.value(); 	break;
			case FINAL: 	facts |= FactVector.FINAL.value(); 		break;
			case PRIVATE: 	facts |= FactVector.PRIVATE.value(); 	break;
			case PUBLIC: 	facts |= FactVector.PUBLIC.value(); 	break;
			case STATIC: 	facts |= FactVector.STATIC.value(); 	break;
			}
		}
		return facts;
	}

}
